#pragma once

#ifndef __MEMORY_MANAGER_H__
#define __MEMORY_MANAGER_H__
#include <string.h>

// IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT 
//
// DO NOT CHANGE THIS HEADER FILE
//
// IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT IMPORTANT 

 const int MM_POOL_SIZE = 65536;

class MemoryPool
{
	struct Head
	{
		bool free;
		int chunkSize;
		Head(size_t size):
		free(true),chunkSize(size) {}
	};
public:
	//---
	//--- CORE Functions, these will need to be completed by the applicant
	//---

	// Initialize any data needed to manage the memory pool
	void initializeMemoryManager(void);



	// return a pointer inside the memory pool
	// If no chunk can accommodate aSize call onOutOfMemory()
	template<class T>
	T* allocate()
	{
		int aSize( sizeof(T) );
		//First fit method
		for(int i=0;i<MM_POOL_SIZE;/*NOTHING*/)
		{	
			Head* iter = (Head*)&MM_pool[i];
			//if we have reached a free chunk and have enough memory to satisfy the request
			if(iter->free&&iter->chunkSize>=aSize)
			{
				//set the header to being used
				iter->free=false;
				//if the chunk size is bigger than the required size and the size of the new head
				if(iter->chunkSize>aSize+static_cast<int>(sizeof(Head)))
				{
					//create a new head at the end of the chunk and set its chunk size
					Head nextHead(iter->chunkSize-aSize-sizeof(Head));
					//copy it into position at the end of the chunk
					memcpy(&MM_pool[i+aSize+sizeof(Head)],&nextHead,sizeof(Head));
					//resize the current headers chunk to match the requested memory
					iter->chunkSize=aSize;
				}
				//return a reference to the start of the chunk
				return (T*)&MM_pool[i+sizeof(Head)];
			}
			else
			{
				//move i along to the next head
				i+=iter->chunkSize+sizeof(Head);
			}
		}
		return 0;
	}


	// Free up a chunk previously allocated
	void deallocate(void* aPointer);

	//---
	//--- support routines
	//--- 

	// Will scan the memory pool and return the total free space remaining
	int freeRemaining(void);

	// Will scan the memory pool and return the largest free space remaining
	int largestFree(void);

	// will scan the memory pool and return the smallest free space remaining
	int smallestFree(void);

	//---
	//--- error conditions. None of these functions will return
	//--- These routines do not need to be implemented by the candidate
	//---

	// Call if no space is left for the allocation request
	void onOutOfMemory(void);

	// Call if a pointer over run condition is detected
	void onOverrunDetected(void);

	// If the caller makes any illegal request your code should call this 
	// provided failure function (which will not return):
	void onIllegalOperation(const char* fmt,...);
	// eg:
	//   int errorCode;
	//   ...
	//   onIllegalOperation("Error in createQueue: %d", errorCode);

	void unfragment();

private:
    char MM_pool[MM_POOL_SIZE];
};


#endif  // __MEMORY_MANAGER_H__
